import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:star_author/config/base_configuration.dart';
import 'package:star_author/utils/utils.dart';

enum LoadMoreListType { normal, custom, nested, listView }

class LoadMoreListWidget extends StatefulWidget {
  LoadMoreListType _type = LoadMoreListType.normal;
  late List<Widget> Function(BuildContext, bool) headerBuilder;
  late Widget Function(BuildContext, int, Map) itemBuilder;
  late Widget Function(List<dynamic>, ScrollController, Widget) childBuilder;
  late List<Widget> Function(List<dynamic>) childrenBuilder;
  late LoadMoreBase sources;
  late Widget? emptyIndicator;
  late bool needInit; //是否需要初始化
  late ScrollPhysics? listViewPhysics;

  LoadMoreListWidget.singleScroll({
    Key? key,
    required this.childrenBuilder,
    required this.sources,
    this.emptyIndicator,
    this.needInit: true,
  }) : super(key: key);

  LoadMoreListWidget.customScroll({
    Key? key,
    required this.childrenBuilder,
    required this.sources,
    this.emptyIndicator,
    this.needInit: true,
  })  : _type = LoadMoreListType.custom,
        super(key: key);

  LoadMoreListWidget.nestedScroll({
    Key? key,
    required this.headerBuilder,
    required this.childBuilder,
    required this.sources,
    this.needInit: true,
    this.emptyIndicator,
  })  : _type = LoadMoreListType.nested,
        super(key: key);

  LoadMoreListWidget.normal({
    Key? key,
    required this.sources,
    this.emptyIndicator,
    required this.childrenBuilder,
    this.needInit: true,
  })  : _type = LoadMoreListType.normal,
        super(key: key);

  LoadMoreListWidget.listView({
    Key? key,
    required this.itemBuilder,
    required this.sources,
    this.emptyIndicator,
    this.needInit: true,
    this.listViewPhysics,
  })  : _type = LoadMoreListType.listView,
        super(key: key);

  _LoadMoreListWidgetState createState() => _LoadMoreListWidgetState();
}

class _LoadMoreListWidgetState extends State<LoadMoreListWidget> {
  bool _loading = false;

  late ScrollController _controller = ScrollController();

  Widget getIndicator() {
    if (!widget.sources.hasMore) {
      return Text(
        '没有更多了',
        style: TextStyle(
          color: ColorConf.lightGray,
          fontSize: 12,
        ),
        textAlign: TextAlign.center,
      );
    } else if (_loading) {
      return Text(
        '正在加载，请稍等...',
        style: TextStyle(
          color: ColorConf.lightGray,
          fontSize: 12,
        ),
        textAlign: TextAlign.center,
      );
    } else {
      return Container();
    }
  }

  late Function() handleScroll = throttle(
    callback: () async {
      double maxHeight = _controller.position.maxScrollExtent;
      double offset = _controller.offset;
      ScrollDirection direction = _controller.position.userScrollDirection;
      if (offset == maxHeight &&
          widget.sources.hasMore &&
          widget.sources.status == LoadMoreStatus.finished) {
        try {
          EasyLoading.show(status: "加载中", dismissOnTap: false);
          _loading = true;
          updateState();
          await widget.sources.loadData();
          _loading = false;
          updateState();
          EasyLoading.dismiss();
        } catch (e) {
          EasyLoading.dismiss();
        }
      }
    },
    delay: Duration(milliseconds: 50),
  );

  void handleSetState() {
    if (widget.sources.isRefresh) _controller.jumpTo(0);
    updateState();
  }

  void updateState() {
    if (mounted) setState(() {});
  }

  void init() async {
    _controller.addListener(handleScroll);
    widget.sources.addListener(handleSetState);
    if (widget.needInit) {
      widget.sources.refreshData(false);
    }
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    init();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    _controller.removeListener(handleScroll);
    widget.sources.removeListener(handleSetState);
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Widget loadIndicator = Padding(
      padding: GetPadding.only(top: 18, bottom: 20),
      child: getIndicator(),
    );
    dynamic body;
    if (widget._type != LoadMoreListType.listView) {
      if (widget._type == LoadMoreListType.nested) {
        body = widget.childBuilder(
            widget.sources.result, _controller, loadIndicator);
      } else {
        body = widget.childrenBuilder(widget.sources.result);
      }
    }
    if (widget.sources.status == LoadMoreStatus.empty) {
      return SingleChildScrollView(
        controller: _controller,
        child: widget.emptyIndicator ?? Container(),
      );
    } else if (widget._type == LoadMoreListType.normal) {
      body.add(loadIndicator);
      return SingleChildScrollView(
        physics: ClampingScrollPhysics(),
        controller: _controller,
        child: Column(
          children: body,
        ),
      );
    } else if (widget._type == LoadMoreListType.custom) {
      (body as List<Widget>).add(SliverToBoxAdapter(
        child: loadIndicator,
      ));
      return CustomScrollView(
        physics: ClampingScrollPhysics(),
        controller: _controller,
        slivers: body,
      );
    } else if (widget._type == LoadMoreListType.nested) {
      return NestedScrollView(
        physics: ClampingScrollPhysics(),
        headerSliverBuilder: widget.headerBuilder,
        body: body,
      );
    } else if (widget._type == LoadMoreListType.listView) {
      return ListView.builder(
        physics: widget.listViewPhysics ?? ClampingScrollPhysics(),
        controller: _controller,
        addAutomaticKeepAlives: false,
        shrinkWrap: true,
        addRepaintBoundaries: false,
        itemBuilder: (ctx, index) {
          if (index < widget.sources.result.length) {
            return widget.itemBuilder(ctx, index, widget.sources.result[index]);
          } else {
            return loadIndicator;
          }
        },
        itemCount: widget.sources.result.length + 1,
      );
    } else {
      return Container();
    }
  }
}

enum LoadMoreStatus { init, loading, finished, empty }

class LoadMoreBase with ChangeNotifier {
  LoadMoreStatus _status = LoadMoreStatus.finished;
  late Function api;
  late Map<String, dynamic>? condition;
  late Map<String, dynamic>? summary;
  late int? perPage;
  List<dynamic> _result = [];
  bool _hasMore = true;
  int _page = 0;
  int _totalPage = 0;
  int _totalCount = 0;
  bool _isRefresh = false;

  LoadMoreStatus get status => _status;

  bool get isRefresh => _isRefresh;

  List<dynamic> get result => _result;

  bool get hasMore => _hasMore;

  int get totalPage => _totalPage;

  int get totalCount => _totalCount;

  int get page => _page;

  LoadMoreBase({
    required this.api,
    this.condition,
    this.perPage = 10,
    this.summary = const {},
  });

  Map<String, int> _getPage() {
    if (_hasMore) {
      _page += 1;
    }
    final page = {
      'page': _page,
      'per_page': perPage!,
    };
    return page;
  }

  void _setData(res) {
    Map<String, dynamic> meta = res.data['meta'];
    _totalPage = meta['total_pages'];
    _totalCount = meta['total_count'];
    _hasMore = _totalPage > meta['current_page'];
    _result.addAll(res.data['data']);
    summary = (res.data['summary']);
    if (_result.length > 0) {
      _status = LoadMoreStatus.finished;
    } else {
      _status = LoadMoreStatus.empty;
    }
    notifyListeners();
  }

  Future loadData() async {
    _isRefresh = false;
    _status = LoadMoreStatus.loading;
    final res = await api({
      ..._getPage(),
      "first_page_timestamp": millisecondsSinceEpoch,
      ...(condition ?? {}),
    });
    _setData(res);
  }

  int millisecondsSinceEpoch = DateTime.now().millisecondsSinceEpoch;

  Future refreshData([bool? keep = false]) async {
    _status = LoadMoreStatus.loading;
    int _perPage;
    if (keep!) {
      _perPage = _page * perPage!;
    } else {
      _perPage = perPage!;
    }
    _hasMore = true;
    if (!keep) _page = 1;
    _totalPage = 0;
    _totalCount = 0;
    _result = [];
    _isRefresh = true;
    int mill = millisecondsSinceEpoch;
    final res = await api({
      'page': _page,
      'per_page': _perPage,
      "first_page_timestamp": mill,
      ...(condition ?? {}),
    });
    _setData(res);
  }

  setCondition(Map<String, dynamic> condition) async {
    this.condition = condition;
    await refreshData(false);
  }

  setConditionNoRefresh(Map<String, dynamic> condition) async {
    this.condition = condition;
  }

  clearResult() {
    _result = [];
  }
}
